package com.cyy.util

import org.apache.commons.betwixt.expression.Context
import org.apache.commons.betwixt.io.BeanReader
import org.apache.commons.betwixt.io.BeanWriter
import org.apache.commons.betwixt.strategy.DecapitalizeNameMapper
import org.apache.commons.betwixt.strategy.DefaultObjectStringConverter
import org.apache.commons.betwixt.strategy.HyphenatedNameMapper
import org.xml.sax.SAXException

import java.beans.IntrospectionException
import java.io.IOException
import java.io.StringReader
import java.io.StringWriter

/**
 * Xml文件与javaBean对象互转工具类
 *
 * @author xufeng
 * @date 2017-11-21下午1:38:56
 */
object XML2BeanUtils {
    private val xmlHead = "<?xml version='1.0' ?>"
    /**
     * 将javaBean对象转换成xml文件，对于没有设置的属性将不会生成xml标签
     * @param obj 待转换的javabean对象
     * @return String 转换后的xml 字符串
     */
    @Throws(IOException::class, SAXException::class, IntrospectionException::class)
    fun bean2XmlString(obj: Any?): String {
        if (obj == null) {
            throw IllegalArgumentException("给定的参数不能为null！")
        }
        val sw = StringWriter()
        sw.write(xmlHead)// 写xml文件头
        val writer = BeanWriter(sw)
        val config = writer.xmlIntrospector.configuration
        val bc = writer.bindingConfiguration
        bc.objectStringConverter = DateConverter()
        bc.mapIDs = false
        config.isAttributesForPrimitives = false
        config.attributeNameMapper = HyphenatedNameMapper()
        config.elementNameMapper = DecapitalizeNameMapper()
        writer.enablePrettyPrint()
        writer.write(obj.javaClass.simpleName, obj)
        writer.close()
        return sw.toString()
    }

    /**
     * 将xml文件转换成相应的javabean对象,对于List，Map，Array转换时需要在需要保证Bean类中有单个添加方法
     * 例如：List<[String]> userNameList --> addUserNameList(String username)
     * String[] items            --> addItems(String item)
     * Map<String></String>,User> userMap               --> addUserMap(String key,User user)
     * 注意：
     * 目前还没有找到好的方法解决Map与Map嵌套，List与List等嵌套的问题，使用时应当避免以上几种情况。
     * @param beanClass 待转换的javabean字节码
     * @param xmlFile xml文件字符串
     * @return 转换后的对象
     */
    @Throws(IntrospectionException::class, IOException::class, SAXException::class)
    fun <T> xmlSring2Bean(beanClass: Class<T>?, xmlFile: String?): T? {
        if (beanClass == null || xmlFile == null || xmlFile.isEmpty()) {
            throw IllegalArgumentException("给定的参数不能为null！")
        }
        val xmlReader = StringReader(xmlFile)
        val reader = BeanReader()
        reader.xmlIntrospector.configuration.isAttributesForPrimitives = false
        val bc = reader.bindingConfiguration
        bc.objectStringConverter = DateConverter()
        bc.mapIDs = false
        var obj: T? = null
        reader.registerBeanClass(beanClass.simpleName, beanClass)
        obj = reader.parse(xmlReader) as T
        xmlReader.close()
        return obj
    }

    /**
     * 日期转换，主要是解决日期为null或者空字符串解析报错问题
     */
    private class DateConverter : DefaultObjectStringConverter() {

        override fun objectToString(obj: Any?, type: Class<*>?, flavour: String?, context: Context?): String {
            return super.objectToString(obj, type, flavour, context)
        }

        override fun stringToObject(string: String?, type: Class<*>?, flavour: String?, context: Context?): Any? {
            return if (string == null || "" == string) {
                null
            } else super.stringToObject(string, type, flavour, context)
        }

        companion object {
            private const val serialVersionUID = -197858851188189916L
        }

    }
}